const path = require("path");

class MyCopyWebpackPlugin {
  constructor(options) {
    this.compiler = null;
    this.patterns = options.patterns;
  }
  // 接收相对路径，返回绝对路径
  resolvePath(relativePath) {
    return path.resolve(this.compiler.options.context, relativePath);
  }

  isDir(absPath) {
    //webpack自身维护了一个方法，this.compiler.inputFileSystem.stat,判断文件的类别,异步函数
    const {
      inputFileSystem: { stat },
    } = this.compiler;
    return new Promise((resolve, reject) => {
      stat(absPath, (err, stats) => {
        if (err) {
          reject(err);
          return;
        }

        if (stats.isDirectory()) {
          resolve(true);
        } else if (stats.isFile()) {
          resolve(false);
        } else {
          reject("unknown error");
        }
      });
    });
  }

  //拷贝文件
  copyFile(compilation, absFrom, to) {
    console.log(absFrom, to, "copyFile");
    //readFile异步函数
    const {
      inputFileSystem: { readFile },
    } = this.compiler;
    //通过this.compiler.webpack.sources可以得到RawSource
    const { RawSource } = this.compiler.webpack.sources;
    return new Promise((resolve, reject) => {
      //如果要拷贝的文件在已生成的文件中存在，直接return,避免把生成的文件覆盖掉了
      const existedAsset = compilation.getAsset(to);
      console.log(existedAsset, "existedAsset");
      if (existedAsset) {
        // compilation.updateAsset(...);
        return;
      }
      readFile(absFrom, (err, data) => {
        if (err) {
          reject(err);
          return;
        }

        const source = new RawSource(data);
        //如果我们要需要通过this.emitAssets来生成文件，必须将我们的asset转成RawSource
        compilation.emitAsset(to, source);
        resolve();
      });
    });
  }

  //拷贝文件夹
  async copyDir(compilation, absFrom, to) {
    // let globby; //是ES module需要动态导入,动态导入需要使用await **/*
    // path.join(pathFrom,'**/*') 代表的是pathFrom下的所有文件夹的所有文件
    try {
      const { globby } = await import("globby");
      let glob = path.join(absFrom, "**/*");
      //需要对系统做兼容 darwin win32
      if (process.platform === "win32") {
        glob = glob.split(path.sep).join("/");
      }
      const globEntries = await globby(glob, {
        objectMode: true, //返回值是一个对象形式
      });

      console.log(globEntries, "globEntries");
      await Promise.all(
        globEntries.map(async (globEntry) => {
          //后者相对于前者的绝对路径
          let pathTo = path.relative(absFrom, globEntry.path);
          pathTo = path.join(to, pathTo);
          //需要对系统做兼容 darwin win32
          // if (process.platform === "win32") {
          //   pathTo = pathTo.split(path.sep).join("/");
          // }
          await this.copyFile(compilation, globEntry.path, pathTo);
        })
      );
    } catch (error) {
      console.log(error);
      return;
    }
  }
  apply(compiler) {
    this.compiler = compiler;
    //上下文 这是webpack.config.js所在的绝对路径
    // console.log(compiler.options.context, "上下文");
    compiler.hooks.thisCompilation.tap("MyCopyWebpackPlugin", (compilation) => {
      compilation.hooks.processAssets.tapAsync(
        { name: "MyCopyWebpackPlugin" },
        async (assets, cb) => {
          this.patterns.forEach(async (pattern) => {
            console.log(pattern, "pattern");
            // from是相对路径，需要转成绝对路径，和上下文一拼接就是绝对路径了
            const { from, to } = pattern;
            const absFrom = this.resolvePath(from);
            try {
              const isDir = await this.isDir(absFrom);
              const pathTo = path.join(to, path.basename(absFrom));

              if (isDir) {
                //文件夹
                this.copyDir(compilation, absFrom, pathTo);
              } else {
                //文件 待拷贝文件与生成文件是否重复 ? updateAsset : emitAsset
                await this.copyFile(compilation, absFrom, pathTo);
              }
            } catch (error) {
              console.log(error);
            }
          });
          //一定要调用这个回调
          cb();
        }
      );
    });
  }
}

module.exports = MyCopyWebpackPlugin;
